/**
 * 
 */
package bingo.validation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;

import bingo.validation.ClassChecks.FieldChecks;
import bingo.validation.annotations.AnnotationValidator;
import bingo.validation.annotations.Check;
import bingo.validation.annotations.Validate;

/**
 * 对象校验器，验证对象的值是否符合对象中定义的约束
 */
public class BeanValidator {
	
	private static final BeanValidator defaultInstance = new BeanValidator();

	private static ConcurrentHashMap<Class<?>, ClassChecks> cachedChecks = new ConcurrentHashMap<Class<?>, ClassChecks>();
	
	/**
	 * 返回BeanValidator缺省实例
	 */
	public static BeanValidator getDefault(){
		return defaultInstance;
	}
	
	/**
	 * 返回对象是否可被验证的
	 * <p>
	 * 一个对象是可被验证的当且只当该类定义了Check Annotation 
	 */
	public boolean isValidatable(Object bean){
		if(null != bean){
			return bean.getClass().isAnnotationPresent(Validate.class);
		}else{
			return false;
		}
	}
	
	/**
	 * 对一个对象进行校验
	 * @throws IllegalgumentException 当一个对象不可被校验，抛出此异常
	 * @throws ValidationException 校验过程中出现非预期的异常 
	 */
	@SuppressWarnings("unchecked")
	public ValidationResults validate(Object bean,boolean stopAfterFirstNotPass) throws ValidationException{
		if(!isValidatable(bean)){
			throw new IllegalArgumentException("bean " + bean + " is not validatable");
		}
		
		ValidationResults results = new ValidationResults();
		
		Class<?> clazz = bean.getClass();
		ClassChecks checks = cachedChecks.get(clazz);
			
		if(null == checks){
			checks = new ClassChecks(clazz);
			
			//type checks 
			for(Annotation anno : clazz.getAnnotations()){
				if(anno.annotationType().isAssignableFrom(Check.class)){
					Check check = (Check)anno;
					if(null != check.validator()){
						Checker checker = createChecker(check.validator());
						if(null != check.message() && !"".equals(check.message().trim())){
							checker.getValidator().setMessage(check.message());
						}
						checks.addTypeChecker(checker);
					}
				}else if(anno.annotationType().isAnnotationPresent(Check.class)){
					Check check = anno.annotationType().getAnnotation(Check.class);
					if(null != check.validator()){
						Checker checker = createChecker(check.validator(), anno);
						if(null != check.message() && !"".equals(check.message().trim())){
							checker.getValidator().setMessage(check.message());
						}
						if(checker.getValidator().getClass().isAssignableFrom(AnnotationValidator.class)){
							((AnnotationValidator<Annotation>)checker.getValidator()).initialize(anno);
						}
						checks.addTypeChecker(checker);
					}else{
						//TODO: warning here
					}
				}
			}
			
			//field checks
			for(Field field : clazz.getDeclaredFields()){
				field.setAccessible(true);
				for(Annotation anno : field.getAnnotations()){
					if(anno.annotationType().isAssignableFrom(Check.class)){
						Check check = (Check)anno;
						if(null != check.validator()){
							Checker checker = createChecker(check.validator());
							if(null != check.message() && !"".equals(check.message().trim())){
								checker.getValidator().setMessage(check.message());
							}
							checks.addFieldCheckers(field,checker);
						}else{
							//TODO: warning here
						}
					}else if(anno.annotationType().isAnnotationPresent(Check.class)){
						Check check = anno.annotationType().getAnnotation(Check.class);
						if(null != check.validator()){
							Checker checker = createChecker(check.validator(), anno);
							if(null != check.message() && !"".equals(check.message().trim())){
								checker.getValidator().setMessage(check.message());
							}	
							if(checker.getValidator() instanceof AnnotationValidator){
								((AnnotationValidator<Annotation>)checker.getValidator()).initialize(anno);
							}						
							checks.addFieldCheckers(field,checker);
						}else{
							//TODO: warning here
						}
					}
				}
			}			
		}
		
		//start validating
		for(Field field : clazz.getDeclaredFields()){
			try {
				FieldChecks fieldChecks = checks.getFieldChecks(field);
				if(null != fieldChecks){
					field.setAccessible(true);
					Object value = field.get(bean);					
					for(Checker checker : fieldChecks.getCheckers()){
						ValidationResult r = checker.getValidator().validate(bean, field, value);
						r.setBeanName(bean.getClass().getSimpleName());
						r.setFieldName(field.getName());
						results.add(r);
						if(stopAfterFirstNotPass && !r.isPass()){
							return results;
						}
					}
				}
			} catch (IllegalAccessException e) {
				throw new ValidationException(e.getMessage(),e);
			}
		}	
		
		for(Checker checker : checks.getTypeCheckers()){
			ValidationResult r = checker.getValidator().validate(bean, bean, bean);
			r.setBeanName(bean.getClass().getSimpleName());
			results.add(r);
			if(stopAfterFirstNotPass && !r.isPass()){
				return results;
			}
		}
		
		return results;
	}
	
	/**
	 * 对一个对象进行校验
	 * @throws IllegalgumentException 当一个对象不可被校验，抛出此异常
	 * @throws ValidationException 校验过程中出现非预期的异常 
	 */
	public ValidationResults validate(Object bean) throws ValidationException{
		return validate(bean,false);
	}	
	
	
	private Checker createChecker(Class<?> validatorClass,Annotation check){
		try {
			IValidator validator = (IValidator)validatorClass.newInstance();
			Checker checker = new Checker(check,validator);
			return checker;
		} catch (InstantiationException e) {
			throw new ValidationException(e.getMessage(),e);
		} catch (IllegalAccessException e) {
			throw new ValidationException(e.getMessage(),e);
		} 
	}
	
	private Checker createChecker(Class<?> validatorClass){
		try {
			IValidator validator = (IValidator)validatorClass.newInstance();
			Checker checker = new Checker(null,validator);
			return checker;
		} catch (InstantiationException e) {
			throw new ValidationException(e.getMessage(),e);
		} catch (IllegalAccessException e) {
			throw new ValidationException(e.getMessage(),e);
		} 
	}
}
